with Ada.Strings;
with Ada.Strings.Unbounded;
with Ada.Streams; use Ada.Streams;
with Ada.Streams.Stream_IO;
with Interfaces; use Interfaces;
with BE_Interfaces; use BE_Interfaces;

package MIDI is

   -- http://www.sonicspot.com/guide/midifiles.html

   subtype Trunk_Name_Type is String(1..4);

   type Trunk_Head_Type is record
      Name : Trunk_Name_Type;
      Size : Uint32_be;
   end record;

   type Var_Length_Type is array(Natural range <>) of Uint8_t
   with Input => Input, Output => Output;

   function To_Uint32 (V : Var_Length_Type) return Uint32_be;

   function To_Var_Length (X : Uint32_be) return Var_Length_Type;

   type Uint24_t is array(Positive range 1..3) of Uint8_t;

   function To_Uint32 (X : Uint24_t) return Uint32_be;

   function To_Uint24 (X : Uint32_be) return Uint24_t;

   type Uint32_vl is new Uint32_be 
   with Input => Input, Output => Output, Read => Read, Write => Output;

   subtype Note_Range is Uint8_t range 0 .. 11;


   Timing_Flag : constant Uint16_be := 2#1000_0000_0000_0000#;

   Timing_Mask : constant Uint16_be := not Timing_Flag;

   type Timing_Type (Metrical : Boolean) is record
      case Metrical is
         when True =>
            Beats_Per_Minute      : Uint16_be;
         when False =>
            Frames_Per_Second     : Uint16_be;
            Sub_Frames_Per_Frame  : Uint16_be;
      end case;
   end record;

   function To_Tick_Div (T : Timing_Type) return Uint16_be;

   function To_Timing (T : Uint16_be) return Timing_Type;

   function To_Microseconds (
      Delta_Time        : Uint32_be;     -- clocks
      Beats_Per_Minutes : Uint16_be;     -- define in MTrk Header
      Clocks_Per_Beat   : Uint8_t        -- see Meta_Events.Meta_Time_Signature
      ) return Uint32_be;

   function To_Microseconds (
      Delta_Time            : Uint32_be; -- clocks
      Microseconds_Per_Beat : Uint32_be; -- see Meta_Events.Meta_Tempo
      Clocks_Per_Beat       : Uint8_t    -- see Meta_Events.Meta_Time_Signature
      ) return Uint32_be;


   type MThd_Trunk_Type is record
      Format   : Uint16_be range 0 .. 2;
      N_Tracks : Uint16_be;
      Tick_Div : Uint16_be;
   end record;


   subtype Channel_Range is Uint8_t range 0 .. 15;

   type Bank_Type is record
      MSB : Uint8_t;
      LSB : Uint8_t;
   end record;

   function To_Uint16 (Bank : Bank_Type) return Uint16_be;

private


   function Input (
      From : not null access Root_Stream_Type'Class
      ) return Var_Length_Type;

   procedure Output (
      To   : not null access Root_Stream_Type'Class;
      From : Var_Length_Type 
      );

   function Input (
      From : not null access Root_Stream_Type'Class
      ) return Uint32_vl;

   procedure Read (
      From : not null access Root_Stream_Type'Class;
      To   : out Uint32_vl
      );

   procedure Output (
      To   : not null access Root_Stream_Type'Class;
      From : Uint32_vl 
      );

end MIDI;
